
#ifndef __UM_HTTP_ABSTRACT_INPUT_BUFFER_H
#define __UM_HTTP_ABSTRACT_INPUT_BUFFER_H

#include <algorithm>
#include "InputStream.hpp"

class AbstractInputStream : public InputStream
{
	static const int N = 1024;

public:

	~AbstractInputStream(void) {
	}

	int read(void* buffer, size_t length) {

		if (length == 0) {
			return 0;
		}

		size_t readBytes = 0;
		while (readBytes < length) {
			if (hasBufferedData()) {
				int n = fill();
				if (n <= 0) {
					break;
				}
			}
			int n = (m_endpos - m_curpos) < (int)length ? (m_endpos - m_curpos) : (int)length;
			memcpy(buffer, m_curpos, n);
			m_curpos += n;
			readBytes += n;
		}

		return readBytes;
	}

	bool readLine(UM::string& line) {
		
		bool retry = true;

		line.clear();
		m_lineBuffer.clear();

		while (true) {

			for (char* strp = m_curpos; strp < m_endpos; ++strp) {
				if (*strp == '\n') {
					if (m_lineBuffer[m_lineBuffer.length() - 1] == '\r') {
						line.append(m_lineBuffer.c_str(), m_lineBuffer.length() - 1);
					}
					else {
						line.append(m_lineBuffer.c_str(), m_lineBuffer.length());
					}
					m_curpos = strp + 1;
					return true;
				}
				else {
					m_lineBuffer.append(strp, 1);
				}
			}

			int n = fill();
			if (n <= 0) {
				return false;
			}

		}

		return false;
	}

protected:

	virtual int fillBuffer(void* buffer, int length) = 0;

private:

	char* locateLF() {
		char* strp;
		for (strp = m_curpos; strp < m_endpos && *strp != '\n'; ++strp) {
		}
		return strp;
	}

	bool hasBufferedData() const {
		return m_curpos == m_endpos;
	}

	int fill() {
		if (m_buffer == 0) {
			m_buffer = new char[N + 1];
			if (m_buffer == 0) {
				return -1;
			}
		}
		int n = fillBuffer(m_buffer, N);
		if (n > 0) {
			m_curpos = m_buffer;
			m_endpos = m_buffer + n;
			m_endpos[0] = 0;
		}
		return n;
	}

	char*		m_buffer;
	char*		m_curpos;
	char*		m_endpos;
	UM::string	m_lineBuffer;
};


#endif // __UM_HTTP_ABSTRACT_INPUT_BUFFER_H